import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

class TireCuringOptimization:
    def __init__(self,num_machines,num_types, tire_quantities):
        self.num_machines = num_machines
        self.num_types = num_types
        self.tire_quantities = tire_quantities
        self.total_tires = sum(self.tire_quantities)
        self.population_size = 120
        self.max_generations = 100
        self.superior_size = self.population_size // 2
        self.initialize_times()
        self.best_fitness = float('inf')
        self.best_individual = None

    def initialize_times(self):
        self.curing_times = np.random.randint(7, 10, (self.num_machines, self.num_types))
        self.changeover_times = np.random.randint(10, 15,self.num_machines)

    def initialize_population(self):
        population = []
        for _ in range(self.population_size):
            individual = []
            for type_index in range(self.num_types):
                machine_assignments = np.random.choice(self.num_machines, self.tire_quantities[type_index],
                                                       replace=True)
                for machine in machine_assignments:
                    individual.append((machine, type_index))
            np.random.shuffle(individual)
            population.append(individual)
        return np.array(population)

    def fitness(self, individual):
        machine_times = np.zeros(self.num_machines)
        last_tire_type = [-1] * self.num_machines
        counts = np.zeros((self.num_machines, self.num_types))
        for machine, tire_type in individual:
            counts[machine, tire_type] += 1
            machine_times[machine] += self.curing_times[machine, tire_type]
            if last_tire_type[machine] != tire_type:
                machine_times[machine] += self.changeover_times[machine]
            last_tire_type[machine] = tire_type
        return max(machine_times), counts

    def update_probability_model(self, superior_population):
        model = np.zeros((self.total_tires, self.num_machines))
        for i in range(self.total_tires):
            for j in range(self.num_machines):
                model[i, j] = np.mean([ind[i][0] == j for ind in superior_population])
        return model

    def generate_new_population(self, model):
        new_population = []
        for _ in range(self.population_size):
            new_individual = []
            type_index = 0
            for i in range(self.total_tires):
                if i >= sum(self.tire_quantities[:type_index + 1]):
                    type_index += 1
                machine = np.random.choice(self.num_machines, p=model[i])
                new_individual.append((machine, type_index))
            new_population.append(new_individual)
        return np.array(new_population)

    def optimize(self):
        population = self.initialize_population()
        fitness_history = []
        for generation in range(self.max_generations):
            fitnesses, counts_list = zip(*[self.fitness(individual) for individual in population])
            superior_indices = np.argsort(fitnesses)[:self.superior_size]
            superior_population = population[superior_indices]
            model = self.update_probability_model(superior_population)
            population = self.generate_new_population(model)
            min_fitness_index = np.argmin(fitnesses)
            if fitnesses[min_fitness_index] < self.best_fitness:
                self.best_fitness = fitnesses[min_fitness_index]
                self.best_individual = population[min_fitness_index]
                best_counts = counts_list[min_fitness_index]
            fitness_history.append(self.best_fitness)
        self.plot_results(fitness_history, best_counts)
        return fitness_history, best_counts, self.best_fitness

    def plot_results(self, fitness_history, best_counts):

        # 创建一个空的figure列表
        figures = []
        figure1, ax1 = plt.subplots(figsize=(3,3))
        ax1.plot(fitness_history, label="总时间")
        ax1.set_xlabel('迭代次数', fontsize=10)
        ax1.set_ylabel('总时间', fontsize=10)
        ax1.tick_params(axis='x', labelsize=10)  # x轴刻度的字体大小
        ax1.tick_params(axis='y', labelsize=10)  # y轴刻度的字体大小
        ax1.legend()
        plt.tight_layout()

        figures.append(figure1)  # 将figure1添加到figure列表中
        # 读取CSV文件
        df = pd.read_csv('curing_sequence_matrix.csv', index_col=0)
        # 绘制每一个列的数据
        for column in df.columns:
            figure2, ax2 = plt.subplots(figsize=(3,3))  # 注意，这里我们为每一个机器创建一个新的图表
            # 取得当前列的数据
            column_data = df[column].values
            plt.bar(df.index, column_data)
            plt.title(f'{column}的轮胎型号分布')
            plt.xlabel('轮胎型号')
            plt.ylabel('硫化轮胎数量')
            plt.xticks(rotation=45)
            plt.tight_layout()
            plt.tight_layout()
            figures.append(figure2)  # 将figure2添加到figures列表中

        self.print_best_solution(self.best_fitness, self.best_individual)

        return figures  # 返回figure的列表

    def print_best_solution(self, best_fitness, best_individual):
        optimal_sequence = [(machine + 1, tire_type + 1) for (machine, tire_type) in best_individual]
        # 从优化序列中获取所有独特的硫化机型号和轮胎型号
        machines = set(machine for machine, tire_type in optimal_sequence)
        tire_types = set(tire_type for machine, tire_type in optimal_sequence)

        # 为硫化机型号添加前缀，并转换为列表
        machine_labels = ['硫化机' + str(machine) for machine in machines]
        # 为轮胎型号添加前缀，并转换为列表
        tire_type_labels = ['轮胎型号' + str(tire_type) for tire_type in tire_types]

        # 计算每台硫化机对应的轮胎型号数量
        curing_counts = {}
        for machine, tire_type in optimal_sequence:
            key = (machine, tire_type)
            if key in curing_counts:
                curing_counts[key] += 1
            else:
                curing_counts[key] = 1

        # 创建一个全为0的DataFrame，索引为带前缀的轮胎型号，列为带前缀的硫化机型号
        curing_matrix = pd.DataFrame(0, index=tire_type_labels, columns=machine_labels)

        for (machine, tire_type), count in curing_counts.items():
            key_machine = '硫化机' + str(machine)
            key_tire_type = '轮胎型号' + str(tire_type)
            curing_matrix.at[key_tire_type, key_machine] = count

        # 填充未分配的部分为0
        curing_matrix = curing_matrix.fillna(0)
        # 保存DataFrame到CSV文件
        curing_matrix.to_csv('curing_sequence_matrix.csv', encoding='utf_8_sig')  # 使用utf_8_sig编码以确保中文显示正确

if __name__ == '__main__':
    # 获取用户输入
    num_machines = int(input("Enter the number of machines: "))
    num_types = int(input("Enter the number of tire types: "))
    tire_quantities = []
    for i in range(num_types):
        quantity = int(input(f"Enter the quantity for tire type {i + 1}: "))
        tire_quantities.append(quantity)
    # 创建优化器实例，传递用户输入
    optimizer = TireCuringOptimization(num_machines, num_types, tire_quantities)
    optimizer.optimize()
